package com.stream.pushstream.stream;

import com.stream.pushstream.web.domain.Device;
import com.stream.pushstream.web.service.IDeviceService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import oshi.SystemInfo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

import static oshi.PlatformEnum.WINDOWS;

@Component
public class FfmpegServer {
    private Logger logger = LoggerFactory.getLogger(FfmpegServer.class);
    @Autowired
    private IDeviceService deviceService;

    public void run() {
        // 获取设备列表
        List<Device> deviceList = deviceService.list();
        // 建立状态维护表
        Map<Long, FfmpegProcessInfo> channelStatusTable = new ConcurrentHashMap<>();
        // 启动服务，初始化状态维护表
        deviceList.forEach((e) -> {
            FfmpegProcessInfo mapInfo = new FfmpegProcessInfo(e.getStreamUrl(), System.currentTimeMillis());
            channelStatusTable.put(e.getUuid(), mapInfo);
            startFfmpegCmd(e.getUuid(), mapInfo);
        });
        // 周期判断当前时间与状态表最后更新时间，超过30秒，根据id进行更新
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                for (Long uuid : channelStatusTable.keySet()) {
                    Long timeDiff = System.currentTimeMillis() - channelStatusTable.get(uuid).lastRecvTime;
                    if (timeDiff > 30 * 1000) {
                        System.out.println("设备[uuid:" + uuid + "]检测到推流服务异常...");

                        try {
                            ProcessHandle parentProcessHandler = channelStatusTable.get(uuid).process.toHandle();
                            Object[] childrenProcessHandler = parentProcessHandler.children().toArray();
                            for (Object processHandle : childrenProcessHandler) {
                                terminateFfmpegProcess(((ProcessHandle)processHandle).pid());
                            }
                            long ppid = parentProcessHandler.pid();
                            terminateFfmpegProcess(parentProcessHandler.pid());
                            System.out.println("设备[uuid:" + uuid + "]退出推流进程[pid:" + ppid + "]");
                            Thread.sleep(3000);
                            startFfmpegCmd(uuid, channelStatusTable.get(uuid));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }, 1000, 5000);
    }

    public void terminateFfmpegProcess(long pid) {
        String[] execParam = new String[]{};
        if (WINDOWS == SystemInfo.getCurrentPlatformEnum()) {
            String cmd = String.format("taskkill /pid %d -f", pid);
            execParam = new String[] {"cmd.exe", "/c", cmd};
        } else {
            String cmd = String.format("kill -9 %d", pid);
            execParam = new String[] {"sh", "-c", cmd};
        }
        try {
            Process process = Runtime.getRuntime().exec(execParam);
        } catch (Exception ex) {
            System.out.println("异常信息-" + ex.getCause());
            ex.printStackTrace();
        }
    }

    public void startFfmpegCmd(long uuid, FfmpegProcessInfo mapInfo) {
        try {
            String cmd = String.format("ffmpeg -rtsp_transport tcp -i \"%s\" -r 20 -q 0 -s 960x540 -f mpegts -codec:v mpeg1video \"http://124.133.2.28:3006/stream/%d\"", mapInfo.streamUrl, uuid);
            String[] execParam = new String[]{};
            if (WINDOWS == SystemInfo.getCurrentPlatformEnum()) {
                execParam = new String[] {"cmd.exe", "/c", cmd};
            } else {
                execParam = new String[] {"sh", "-c", cmd};
            }
            Process process = Runtime.getRuntime().exec(execParam);
            System.out.println("设备[uuid:" + uuid + "]启动推流服务...");
            mapInfo.process = process;
            mapInfo.lastRecvTime = System.currentTimeMillis();
            new Thread(() -> {
                BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()));
                try{
                    String str;
                    while( (str = br.readLine()) != null ) {
                        System.out.println("设备[uuid:" + uuid + "]输出-" + str);
                        // 更新当前时间
                        mapInfo.lastRecvTime = System.currentTimeMillis();
                    }
                }
                catch(Exception ex) {
                    ex.printStackTrace();
                } finally {
                    try {
                        br.close();
                        process.getErrorStream().close();
                        process.getInputStream().close();
                        process.getOutputStream().close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        } catch (Exception ex) {
            System.out.println("设备[uuid:" + uuid + "]异常信息-" + ex.getMessage() + ex.getCause());
            ex.printStackTrace();
        }
    }
}
class FfmpegProcessInfo {
    public FfmpegProcessInfo(String streamUrl, Long lastRecvTime) {
        this.streamUrl = streamUrl;
        this.lastRecvTime = lastRecvTime;
        this.process = null;
    }

    public String streamUrl;
    public Long lastRecvTime;
    public Process process;
}
